using System;
using System.Collections;

namespace Neural
{
	/// <summary>
	/// Multi layer feed-forward network
	/// </summary>
	public abstract class FeedForward: Network
	{
		
		#region helper classes

		/// <summary>
		/// Indexer class for layers
		/// </summary>
		public class LayerList
		{
			private ArrayList list;

			public LayerList(ArrayList list)
			{
				this.list = list;
			}

			public int Count
			{
				get
				{
					return list.Count;
				}
			}

			public Layer this[int index]
			{
				get
				{
					return (Layer)list[index];
				}
			}
		}

		#endregion

		#region fields

		/**************************************************************/
		/*                      Fields
		/**************************************************************/
		
		protected ArrayList p_Layers;
		protected LayerList m_Indexer;

		#endregion

		#region properties

		/**************************************************************/
		/*                      Properties
		/**************************************************************/

		/// <summary>
		/// Layers in this network
		/// </summary>
		public LayerList Layers
		{
			get
			{
				return m_Indexer;
			}
		}

		/// <summary>
		/// Current activaion function
		/// </summary>
		public override IActivation Activation
		{
			set
			{
				m_Activation = value;
				foreach (Layer layer in p_Layers)
					layer.Activation = m_Activation;
			}
		}

		#endregion

		#region constructors

		/**************************************************************/
		/*                      Constructors
		/**************************************************************/

		public FeedForward(int inputs, int[] layers, int outputs): base(inputs, outputs)
		{	
			this.p_Layers = new ArrayList();
			this.m_Indexer = new LayerList(this.p_Layers); /* layer indexer */
			
			/*
			 * Create optional hidden layers
			 */
			if (layers != null)
			{
				for (int i = 0; i < layers.Length - 1; i++)
				{
					if (layers[i] > 0)
					{
						p_Layers.Add(new Layer(inputs, layers[i], m_Activation));
						inputs = layers[i];
					}
				}
			}

			/* Create an output layer */
			p_Layers.Add(new Layer(inputs, outputs, m_Activation));
		}

		#endregion

		#region methods

		/**************************************************************/
		/*                      Methods
		/**************************************************************/

		/// <summary>
		/// Randomizes all weights in the network
		/// </summary>
		public override void Randomize()
		{
			System.Random engine = new System.Random();
			foreach (Layer layer in p_Layers)
				layer.Randomize(engine);
		}

		/// <summary>
		/// Propagates inputs
		/// </summary>
		public override void Run()
		{
			/*
			 * Propagate inputs from one layer to the next
			 */
			double[] inputs = m_Inputs;
			foreach (Layer layer in p_Layers)
			{
				layer.Propagate(inputs);
				inputs = layer.Outputs;
			}

			if (inputs.Length != m_Outputs.Length)
				throw new ArgumentException("Invalid output vector");
			inputs.CopyTo(m_Outputs, 0);
		}

		#endregion

	}
}
